<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>检测数据类型</title>
</head>
<body>
    <script>
        //1、 typeof 用来检测数据类型的运算符
        // console.log(typof (typof (typof (function(){})))) //string
        // typeof (null) // "object"
        
        function fn(num1, num2) {
            // if(typeof num2 === "undefined"){
            //     num2 = 0
            // }
            num2 = num2 || 0
        }

        function fn (callback) {
            typeof callback === "function" ? callback():null;
            callback && callback();
        }

        
    </script>
    <script>
        //2、 instanceof 检测某一个实例是否属于某个类

            // list 1. instanceof 示例
            var oStringObject = new String("hello world");
            console.log(oStringObject instanceof String);   // 输出 "true"
            // 这段代码问的是“变量 oStringObject 是否为 String 对象的实例？”oStringObject 的确是 String 对象的实例，因此结果是"true"。尽管不像 typeof 方法那样灵活，但是在 typeof 方法返回 "object" 的情况下，instanceof 方法还是很有用的。

            // list 2. 
            function Foo() { }
            var foo = new Foo();
            console.log(foo instanceof Foo)//true

            // list 3. 
            // 判断 foo 是否是 Foo 类的实例 , 并且是否是其父类型的实例
            function Aoo() { }
            function Foo() { }
            Foo.prototype = new Aoo();//JavaScript 原型继承

            var foo = new Foo();
            console.log(foo instanceof Foo)//true 
            console.log(foo instanceof Aoo)//true
            // list 4.
            console.log(Object instanceof Object);//true 
            console.log(Function instanceof Function);//true 
            console.log(Number instanceof Number);//false 
            console.log(String instanceof String);//false 

            console.log(Function instanceof Object);//true 

            console.log(Foo instanceof Function);//true 
            console.log(Foo instanceof Foo);//false
            // list 5.
            function instance_of(L, R) {//L 表示左表达式，R 表示右表达式
                var O = R.prototype;// 取 R 的显示原型
                L = L.__proto__;// 取 L 的隐式原型
                while (true) {
                    if (L === null)
                        return false;
                    if (O === L)// 这里重点：当 O 严格等于 L 时，返回 true 
                        return true;
                    L = L.__proto__;
                }
            }
            // list 6. Dojo 中多重继承
                // dojo.declare("Aoo", null, {});
                // dojo.declare("Boo", null, {});
                // dojo.declare("Foo", [Aoo, Boo], {});

                // var foo = new Foo();
                // console.log(foo instanceof Aoo);//true 
                // console.log(foo instanceof Boo);//false 

                // console.log(foo.isInstanceOf(Aoo));//true 
                // console.log(foo.isInstanceOf(Boo));//true
    </script>
    <script>
        //3、 constructor 构造函数
        var obj = {}
        console.log(obj.constructor === Array); //true
        console.log(obj.constructor === RegExp); //false
        // constructor可以处理基本数据类型的检测
        // var num = 1;
        // console.log(num.constructor===Number)// true
        var reg = /^$/;
        console.log(reg.constructor === RegExp)// true
        console.log(reg.constructor === Object)//false

        // 局限性
        // 当把类的原型进行重写，
        // 在重写的过程中很有可能出现把之前的constructor给覆盖了
        // 这样检测出来的结果就是不准确的
        function Fn() {}
        Fn.prototype = new Array;
        var f = new Fn;
        console.log(f.constructor); // -> Array

    </script>
    <script>
        // 最准确最常用的方式
        //4、 Object.prototype.toString.call() (函数数据类)
        // (128).toString(2/8/10)
        // (true).toString()
        // (null).toString()
        // [1,2,3,4].toString() //1,2,3,4
        // new Date().toString()
        // 
        Object.prototype.toString.call(null); // "[object Null]"
        Object.prototype.toString.call(undefined); // "[object Undefined]"
        Object.prototype.toString.call("abc");// "[object String]"
        Object.prototype.toString.call(123);// "[object Number]"
        Object.prototype.toString.call(true);// "[object Boolean]"

        function fn(){
            console.log("test");
        }
        Object.prototype.toString.call(fn); // "[object Function]"
    
        var date = new Date();
        Object.prototype.toString.call(date); // "[object Date]"
        
        var arr = [1, 2, 3];
        Object.prototype.toString.call(arr); // "[object Array]"
    
        var reg = /[hbc]at/gi;
        Object.prototype.toString.call(reg); // "[object RegExp]"
    
        //**自定义类型**
        function Person(name, age) {
            this.name = name;
            this.age = age;
        }
        var person = new Person("Rose", 18);
        Object.prototype.toString.call(arr); // "[object Object]"
        
        var isNativeJSON = window.JSON && Object.prototype.toString.call(JSON);
        console.log(isNativeJSON);// 输出结果为”[object JSON]”说明JSON是原生的，否则不是；
    </script>
    <script>
    // 为Array对象添加一个去除重复项的方法

    // input
    // [false, true, undefined, null, NaN, 0, 1, {}, {}, 'a', 'a', NaN].uniq()
    // output
    // [false, true, undefined, null, NaN, 0, 1, {}, {}, 'a']
    
    // 这里要注意，NaN === NaN 为false，{} === {}为false。
    
    // 1)
    Array.prototype.uniq = function () {
            if (!this.length || this.length == 0) return this;
            var res = [], key, hasNaN = false, temp = {};
            for (var i = 0; i < this.length; i++) {
                if (typeof this[i] === 'object') {
                    res.push(this[i]);
                } else if (this[i] != this[i]) { // 如果当前遍历元素是NaN
                    if (!hasNaN) {
                        res.push(this[i]);
                        hasNaN = true;
                    }
                } else {
                    key = typeof (this[i]) + this[i];
                    if (!temp[key]) {
                        res.push(this[i]);
                        temp[key] = true;
                    }
                }
            }
            return res;
        }
    
    // 2)
    Array.prototype.uniq = function () {
        var res = [];
        var flag = true;
        this.forEach(function (x) {
            if (res.indexOf(x) == -1) {
                if (x != x) {
                    if (flag) {
                        res.push(x);
                        flag = false;
                    }
                } else {
                    res.push(x);
                }
            }
        })
        return res;
    }
    
    </script>
    
</body>
</html>